package cn.bore.web.controller;

import java.time.Duration;
import java.util.LinkedHashMap;

import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.bore.common.core.constant.Constants;
import cn.bore.common.core.constant.GlobalConstants;
import cn.bore.common.core.domain.R;
import cn.bore.common.core.exception.ServiceException;
import cn.bore.common.core.utils.SpringUtils;
import cn.bore.common.core.utils.StringUtils;
import cn.bore.common.core.utils.reflect.ReflectUtils;
import cn.bore.common.mail.config.properties.MailProperties;
import cn.bore.common.mail.utils.MailUtils;
import cn.bore.common.ratelimiter.annotation.RateLimiter;
import cn.bore.common.ratelimiter.enums.LimitType;
import cn.bore.common.redis.utils.RedisUtils;
import cn.bore.common.web.config.properties.CaptchaProperties;
import cn.bore.common.web.enums.CaptchaType;
import cn.bore.web.domain.vo.CaptchaVo;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 验证码操作处理
 *
 * @author Lion Li
 */
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {

	private final CaptchaProperties captchaProperties;
	private final MailProperties mailProperties;

	/**
	 * 短信验证码
	 *
	 * @param phonenumber 用户手机号
	 */
	@RateLimiter(key = "#phonenumber", time = 60, count = 1)
	@GetMapping("/resource/sms/code")
	public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
		String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
		String code = RandomUtil.randomNumbers(4);
		RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
		// 验证码模板id 自行处理 (查数据库或写死均可)
		String templateId = "";
		LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
		map.put("code", code);
		SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
		SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
		if (!smsResponse.isSuccess()) {
			log.error("验证码短信发送异常 => {}", smsResponse);
			return R.fail(smsResponse.getData().toString());
		}
		return R.ok();
	}

	/**
	 * 邮箱验证码
	 *
	 * @param email 邮箱
	 */
	@GetMapping("/resource/email/code")
	public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
		if (!mailProperties.getEnabled()) {
			return R.fail("当前系统没有开启邮箱功能！");
		}
		SpringUtils.getAopProxy(this).emailCodeImpl(email);
		return R.ok();
	}

	/**
	 * 邮箱验证码 独立方法避免验证码关闭之后仍然走限流
	 */
	@RateLimiter(key = "#email", time = 60, count = 1)
	public void emailCodeImpl(String email) {
		String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
		String code = RandomUtil.randomNumbers(4);
		RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
		try {
			MailUtils.sendText(email, "登录验证码",
					"您本次验证码为：" + code + "，有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟，请尽快填写。");
		} catch (Exception e) {
			log.error("验证码短信发送异常 => {}", e.getMessage());
			throw new ServiceException(e.getMessage());
		}
	}

	/**
	 * 生成验证码
	 */
	@GetMapping("/auth/code")
	public R<CaptchaVo> getCode() {
		boolean captchaEnabled = captchaProperties.getEnable();
		if (!captchaEnabled) {
			CaptchaVo captchaVo = new CaptchaVo();
			captchaVo.setCaptchaEnabled(false);
			return R.ok(captchaVo);
		}
		return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
	}

	/**
	 * 生成验证码 独立方法避免验证码关闭之后仍然走限流
	 */
	@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
	public CaptchaVo getCodeImpl() {
		// 保存验证码信息
		String uuid = IdUtil.simpleUUID();
		String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
		// 生成验证码
		CaptchaType captchaType = captchaProperties.getType();
		boolean isMath = CaptchaType.MATH == captchaType;
		Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
		CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
		AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
		captcha.setGenerator(codeGenerator);
		captcha.createCode();
		// 如果是数学验证码，使用SpEL表达式处理验证码结果
		String code = captcha.getCode();
		if (isMath) {
			ExpressionParser parser = new SpelExpressionParser();
			Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
			code = exp.getValue(String.class);
		}
		RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
		CaptchaVo captchaVo = new CaptchaVo();
		captchaVo.setUuid(uuid);
		captchaVo.setImg(captcha.getImageBase64());
		return captchaVo;
	}

}
